#include "WebKeyboardEventWrapper.h"

ref class WebView;

namespace AwesomiumDotNet
{
	/// <summary>
	/// Mouse button enumerations,
	/// used with WebView.InjectMouseDown and WebView.InjectMouseUp
	/// </summary>
	public enum class MouseButton
	{
		Left, Right, Middle
	};
	
#ifdef VERSION_108
	delegate void BeginLoadingDelegate (const std::string& url, const std::wstring& frameName, int statusCode, const std::wstring& mimeType);
	delegate void BeginNavigationDelegate (const std::string& url, const std::wstring& frameName);
	delegate void CallbackDelegate (const std::string& name, const Awesomium::JSArguments& args);
	delegate void ChangeCursorDelegate (const HCURSOR& cursor);
	delegate void ChangeKeyboardFocusDelegate (bool isFocused);
	delegate void ChangeTargetUrlDelegate (const std::string& url);
	delegate void ChangeTooltipDelegate (const std::wstring& tooltip);
	delegate void FinishLoadingDelegate ();
	delegate void ReceiveTitleDelegate (const std::wstring& title, const std::wstring& frameName);
#else
	delegate void BeginLoadingDelegate (Awesomium::WebView* caller, const std::string& url, const std::wstring& frameName, int statusCode, const std::wstring& mimeType);
	delegate void BeginNavigationDelegate (Awesomium::WebView* caller, const std::string& url, const std::wstring& frameName);
	delegate void CallbackDelegate (Awesomium::WebView* caller, const std::wstring& objectName, const std::wstring& callbackName, const Awesomium::JSArguments& args);
	delegate void ChangeCursorDelegate (Awesomium::WebView* caller, const HCURSOR& cursor);
	delegate void ChangeKeyboardFocusDelegate (Awesomium::WebView* caller, bool isFocused);
	delegate void ChangeTargetUrlDelegate (Awesomium::WebView* caller, const std::string& url);
	delegate void ChangeTooltipDelegate (Awesomium::WebView* caller, const std::wstring& tooltip);
	delegate void FinishLoadingDelegate (Awesomium::WebView* caller);
	delegate void ReceiveTitleDelegate (Awesomium::WebView* caller, const std::wstring& title, const std::wstring& frameName);
	delegate void OpenExternalLinkDelegate (Awesomium::WebView* caller, const std::string& url, const std::wstring& source);
#endif

	/*
	Delegate wrapper structures for converting the
	managed delegates to unmanaged function pointers.
	*/
	value struct BeginLoadingWrapper {
		[MarshalAsAttribute(UnmanagedType::FunctionPtr)]
		BeginLoadingDelegate^ _Delegate; };
	value struct BeginNavigationWrapper {
		[MarshalAsAttribute(UnmanagedType::FunctionPtr)]
		BeginNavigationDelegate^ _Delegate; };
	value struct CallbackWrapper {
		[MarshalAsAttribute(UnmanagedType::FunctionPtr)]
		CallbackDelegate^ _Delegate; };
	value struct ChangeCursorWrapper {
		[MarshalAsAttribute(UnmanagedType::FunctionPtr)]
		ChangeCursorDelegate^ _Delegate; };
	value struct ChangeKeyboardFocusWrapper {
		[MarshalAsAttribute(UnmanagedType::FunctionPtr)]
		ChangeKeyboardFocusDelegate^ _Delegate; };
	value struct ChangeTargetUrlWrapper {
		[MarshalAsAttribute(UnmanagedType::FunctionPtr)]
		ChangeTargetUrlDelegate^ _Delegate; };
	value struct ChangeTooltipWrapper {
		[MarshalAsAttribute(UnmanagedType::FunctionPtr)]
		ChangeTooltipDelegate^ _Delegate; };
	value struct FinishLoadingWrapper {
		[MarshalAsAttribute(UnmanagedType::FunctionPtr)]
		FinishLoadingDelegate^ _Delegate; };
	value struct ReceiveTitleWrapper {
		[MarshalAsAttribute(UnmanagedType::FunctionPtr)]
		ReceiveTitleDelegate^ _Delegate; };
#ifndef VERSION_108
	value struct OpenExternalLinkWrapper {
		[MarshalAsAttribute(UnmanagedType::FunctionPtr)]
		OpenExternalLinkDelegate^ _Delegate; };
#endif

	/// <summary>
	/// A WebView is essentially a single instance of a web-browser
	/// (created via the WebCore singleton) that you can interact with
	/// (via input injection, javascript, etc.) and render to an off-screen buffer.
	/// </summary>
	public ref class WebView
	{

	private:
		Awesomium::WebView* webView;
		MyWebViewListener* listener;

		BeginLoadingWrapper^ d_BeginLoading;
		BeginNavigationWrapper^ d_BeginNavigation;
		CallbackWrapper^ d_Callback;
		ChangeCursorWrapper^ d_ChangeCursor;
		ChangeKeyboardFocusWrapper^ d_ChangeKeyboardFocus;
		ChangeTargetUrlWrapper^ d_ChangeTargetURL;
		ChangeTooltipWrapper^ d_ChangeTooltip;
		FinishLoadingWrapper^ d_FinishLoading;
		ReceiveTitleWrapper^ d_ReceiveTitle;
#ifndef VERSION_108
		OpenExternalLinkWrapper^ d_OpenExternalLink;
#endif

	public:
		/// <summary>This event is fired when a WebView begins to actually receive data from a server.</summary>
		event EventHandler<BeginLoadingEventArgs^>^ BeginLoading;

		/// <summary>This event is fired when a WebView begins navigating to a new URL.</summary>
		event EventHandler<BeginNavigationEventArgs^>^ BeginNavigation;

		/// <summary>This event is fired when a Client callback has been invoked via Javascript from a page.</summary>
		event EventHandler<CallbackEventArgs^>^ Callback;

		/// <summary>This event is fired when a cursor has changed state.</summary>
		event EventHandler<ChangeCursorEventArgs^>^ ChangeCursor;

		/// <summary>This event is fired when keyboard focus has changed.</summary>
		event EventHandler<ChangeKeyboardFocusEventArgs^>^ ChangeKeyboardFocus;
		
		/// <summary>This event is fired when the target URL has changed. This is usually the result of
		/// hovering over a link on the page.</summary>
		event EventHandler<ChangeTargetUrlEventArgs^>^ ChangeTargetUrl;

		/// <summary>This event is fired when a tooltip has changed state.</summary>
		event EventHandler<ChangeTooltipEventArgs^>^ ChangeTooltip;

		/// <summary>This event is fired when all loads have finished for a WebView.</summary>
		event EventHandler<FinishLoadingEventArgs^>^ FinishLoading;

		/// <summary>This event is fired when a page title is received.</summary>
		event EventHandler<ReceiveTitleEventArgs^>^ ReceiveTitle;

#ifndef VERSION_108
		/// <summary>This event is fired when an external link is attempted to be opened. An external link
		/// is any link that normally opens in a new window in a standard browser (for example, links
		/// with target="_blank", calls to window.open(url), and URL open events from Flash plugins).
		/// External links may or may not be automatically opened by a WebView (please see
		/// WebView::setOpensExternalLinksInCallingFrame).</summary>
		event EventHandler<OpenExternalLinkEventArgs^>^ OpenExternalLink;
#endif

		WebView (Awesomium::WebCore* webCore,
			int width, int height)
		{
			webView = webCore->createWebView(width, height);
			WebViewInit();
		}

		WebView (Awesomium::WebCore* webCore,
			int width, int height,
			bool isTransparent)
		{
			webView = webCore->createWebView(width, height, isTransparent);
			WebViewInit();
		}

		WebView (Awesomium::WebCore* webCore,
			int width, int height,
			bool isTransparent,
			bool enableAsyncRendering)
		{
			webView = webCore->createWebView(width, height, isTransparent, enableAsyncRendering);
			WebViewInit();
		}

		WebView (Awesomium::WebCore* webCore,
			int width, int height,
			bool isTransparent,
			bool enableAsyncRendering,
			int maxAsyncRenderPerSec)
		{
			webView = webCore->createWebView(width, height,
				isTransparent, enableAsyncRendering, maxAsyncRenderPerSec);
			WebViewInit();
		}

		virtual ~WebView()
		{
			if (webView != 0)
			{
				webView->setListener(0);
				webView->destroy();
				webView = 0;
			}
		}

	private:
		void WebViewInit ()
		{
			listener = new MyWebViewListener();

			d_BeginNavigation = gcnew BeginNavigationWrapper();
			d_BeginLoading = gcnew BeginLoadingWrapper();
			d_FinishLoading = gcnew FinishLoadingWrapper();
			d_Callback = gcnew CallbackWrapper();
			d_ReceiveTitle = gcnew ReceiveTitleWrapper();
			d_ChangeTooltip = gcnew ChangeTooltipWrapper();
			d_ChangeCursor = gcnew ChangeCursorWrapper();
			d_ChangeKeyboardFocus = gcnew ChangeKeyboardFocusWrapper();
			d_ChangeTargetURL = gcnew ChangeTargetUrlWrapper();
#ifndef VERSION_108
			d_OpenExternalLink = gcnew OpenExternalLinkWrapper();
#endif

			d_BeginNavigation->_Delegate =
				gcnew BeginNavigationDelegate(this, &WebView::onBeginNavigation);
			d_BeginLoading->_Delegate =
				gcnew BeginLoadingDelegate(this, &WebView::onBeginLoading);
			d_FinishLoading->_Delegate =
				gcnew FinishLoadingDelegate(this, &WebView::onFinishLoading);
			d_Callback->_Delegate =
				gcnew CallbackDelegate(this, &WebView::onCallback);
			d_ReceiveTitle->_Delegate =
				gcnew ReceiveTitleDelegate(this, &WebView::onReceiveTitle);
			d_ChangeTooltip->_Delegate =
				gcnew ChangeTooltipDelegate(this, &WebView::onChangeTooltip);
			d_ChangeCursor->_Delegate =
				gcnew ChangeCursorDelegate(this, &WebView::onChangeCursor);
			d_ChangeKeyboardFocus->_Delegate =
				gcnew ChangeKeyboardFocusDelegate(this, &WebView::onChangeKeyboardFocus);
			d_ChangeTargetURL->_Delegate =
				gcnew ChangeTargetUrlDelegate(this, &WebView::onChangeTargetUrl);
#ifndef VERSION_108
			d_OpenExternalLink->_Delegate =
				gcnew OpenExternalLinkDelegate(this, &WebView::onOpenExternalLink);
#endif

			Marshal::StructureToPtr (d_BeginLoading, (IntPtr)&listener->cb_onBeginLoading, false);
			Marshal::StructureToPtr (d_BeginNavigation, (IntPtr)&listener->cb_onBeginNavigation, false);
			Marshal::StructureToPtr (d_Callback, (IntPtr)&listener->cb_onCallback, false);
			Marshal::StructureToPtr (d_ChangeCursor, (IntPtr)&listener->cb_onChangeCursor, false);
			Marshal::StructureToPtr (d_ChangeKeyboardFocus, (IntPtr)&listener->cb_onChangeKeyboardFocus, false);
			Marshal::StructureToPtr (d_ChangeTargetURL, (IntPtr)&listener->cb_onChangeTargetURL, false);
			Marshal::StructureToPtr (d_ChangeTooltip, (IntPtr)&listener->cb_onChangeTooltip, false);
			Marshal::StructureToPtr (d_FinishLoading, (IntPtr)&listener->cb_onFinishLoading, false);
			Marshal::StructureToPtr (d_ReceiveTitle, (IntPtr)&listener->cb_onReceiveTitle, false);
#ifndef VERSION_108
			Marshal::StructureToPtr (d_OpenExternalLink, (IntPtr)&listener->cb_onOpenExternalLink, false);
#endif

			webView->setListener(listener);
		}

#ifdef VERSION_108
				void onBeginLoading (const std::string& url, const std::wstring& frameName, int statusCode, const std::wstring& mimeType)
		{
			BeginLoadingEventArgs^ args = gcnew BeginLoadingEventArgs(
				gcnew String(url.c_str()),
				gcnew String(frameName.c_str()),
				statusCode,
				gcnew String(mimeType.c_str()) );
			BeginLoading(this, args);
		}

		void onBeginNavigation (const std::string& url, const std::wstring& frameName)
		{
			BeginNavigationEventArgs^ args = gcnew BeginNavigationEventArgs(
				gcnew String(url.c_str()),
				gcnew String(frameName.c_str()) );
			BeginNavigation(this, args);
		}

		void onCallback (const std::string& name, const Awesomium::JSArguments& in_args)
		{
			Collection<JSValue^>^ out_args = gcnew Collection<JSValue^>();
			for each (Awesomium::JSValue value in in_args)
			{
				out_args->Add(gcnew JSValue(value));
			}
			CallbackEventArgs^ cb_args = gcnew CallbackEventArgs(
				gcnew String(name.c_str()),
				out_args
			);
			Callback(this, cb_args);
		}

		void onChangeCursor (const HCURSOR& cursor)
		{
			ChangeCursorEventArgs^ args = gcnew ChangeCursorEventArgs(
				gcnew Cursor((IntPtr)cursor)
				);
			ChangeCursor(this, args);
		}

		void onChangeKeyboardFocus (bool isFocused)
		{
			ChangeKeyboardFocusEventArgs^ args = gcnew ChangeKeyboardFocusEventArgs(
				isFocused );
			ChangeKeyboardFocus(this, args);
		}

		void onChangeTargetUrl (const std::string& url)
		{
			ChangeTargetUrlEventArgs^ args = gcnew ChangeTargetUrlEventArgs(
				gcnew String(url.c_str())
				);
			ChangeTargetUrl(this, args);
		}

		void onChangeTooltip (const std::wstring& tooltip)
		{
			ChangeTooltipEventArgs^ args = gcnew ChangeTooltipEventArgs(
				gcnew String(tooltip.c_str()) );
			ChangeTooltip(this, args);
		}

		void onFinishLoading ()
		{
			FinishLoading(this, gcnew FinishLoadingEventArgs());
		}

		void onReceiveTitle (const std::wstring& title, const std::wstring& frameName)
		{
			ReceiveTitleEventArgs^ args = gcnew ReceiveTitleEventArgs(
				gcnew String(title.c_str()),
				gcnew String(frameName.c_str()) );
			ReceiveTitle(this, args);
		}
#else
		void onBeginLoading (Awesomium::WebView* caller, const std::string& url, const std::wstring& frameName, int statusCode, const std::wstring& mimeType)
		{
			BeginLoadingEventArgs^ args = gcnew BeginLoadingEventArgs(
				gcnew String(url.c_str()),
				gcnew String(frameName.c_str()),
				statusCode,
				gcnew String(mimeType.c_str()) );
			BeginLoading(this, args);
		}

		void onBeginNavigation (Awesomium::WebView* caller, const std::string& url, const std::wstring& frameName)
		{
			BeginNavigationEventArgs^ args = gcnew BeginNavigationEventArgs(
				gcnew String(url.c_str()),
				gcnew String(frameName.c_str()) );
			BeginNavigation(this, args);
		}

		void onCallback (Awesomium::WebView* caller, const std::wstring& objectName, const std::wstring& callbackName, const Awesomium::JSArguments& in_args)
		{
			Collection<JSValue^>^ out_args = gcnew Collection<JSValue^>();
			for each (Awesomium::JSValue value in in_args)
			{
				out_args->Add(gcnew JSValue(value));
			}
			CallbackEventArgs^ cb_args = gcnew CallbackEventArgs(
				gcnew String(objectName.c_str()),
				gcnew String(callbackName.c_str()),
				out_args
			);
			Callback(this, cb_args);
		}

		void onChangeCursor (Awesomium::WebView* caller, const HCURSOR& cursor)
		{
			ChangeCursorEventArgs^ args = gcnew ChangeCursorEventArgs(
				gcnew Cursor((IntPtr)cursor)
				);
			ChangeCursor(this, args);
		}

		void onChangeKeyboardFocus (Awesomium::WebView* caller, bool isFocused)
		{
			ChangeKeyboardFocusEventArgs^ args = gcnew ChangeKeyboardFocusEventArgs(
				isFocused );
			ChangeKeyboardFocus(this, args);
		}

		void onChangeTargetUrl (Awesomium::WebView* caller, const std::string& url)
		{
			ChangeTargetUrlEventArgs^ args = gcnew ChangeTargetUrlEventArgs(
				gcnew String(url.c_str())
				);
			ChangeTargetUrl(this, args);
		}

		void onChangeTooltip (Awesomium::WebView* caller, const std::wstring& tooltip)
		{
			ChangeTooltipEventArgs^ args = gcnew ChangeTooltipEventArgs(
				gcnew String(tooltip.c_str()) );
			ChangeTooltip(this, args);
		}

		void onFinishLoading (Awesomium::WebView* caller)
		{
			FinishLoading(this, gcnew FinishLoadingEventArgs());
		}

		void onReceiveTitle (Awesomium::WebView* caller, const std::wstring& title, const std::wstring& frameName)
		{
			ReceiveTitleEventArgs^ args = gcnew ReceiveTitleEventArgs(
				gcnew String(title.c_str()),
				gcnew String(frameName.c_str()) );
			ReceiveTitle(this, args);
		}

		void onOpenExternalLink (Awesomium::WebView* caller, const std::string& url, const std::wstring& source)
		{
			OpenExternalLinkEventArgs^ args = gcnew OpenExternalLinkEventArgs(
				gcnew String(url.c_str()),
				gcnew String(source.c_str()) );
			OpenExternalLink(this, args);
		}
#endif


	// WebView functions

	public:
		/// <summary>
		/// Explicitly destroys this WebView instance. If you neglect to call this,
		/// the WebCore singleton will automatically destroy all lingering WebView instances at shutdown.
		/// </summary>
		/// <remark>
		/// This should NEVER be called directly from one of the notifications of WebViewListener.
		/// </remark>
		void ::WebView::Destroy ()
		{
			delete this;
		}

		/// <summary>
		/// Notifies the current page that is has gained focus.
		/// </summary>
		void ::WebView::Focus ()
		{
			webView->focus();
		}

		/// <summary>
		/// Notifies the current page that it has lost focus.
		/// </summary>
		void ::WebView::Unfocus ()
		{
			webView->unfocus();
		}

		/// <summary>
		/// Invokes a 'copy' action using the system clipboard.
		/// </summary>
		void ::WebView::Copy ()
		{
			webView->copy();
		}

		/// <summary>
		/// Invokes a 'cut' action using the system clipboard.
		/// </summary>
		void ::WebView::Cut ()
		{
			webView->cut();
		}

		/// <summary>
		/// Invokes a 'paste' action using the system clipboard.
		/// </summary>
		void ::WebView::Paste ()
		{
			webView->paste();
		}


		/// <summary>
		/// Selects all items on the current page.
		/// </summary>
		void ::WebView::SelectAll ()
		{
			webView->selectAll();
		}

		/// <summary>
		/// Deselects all items on the current page.
		/// </summary>
		void ::WebView::DeselectAll ()
		{
			webView->deselectAll();
		}

		/// <summary>
		/// Retrieves the content of the current page as plain text.
		/// </summary>
		/// <param name="maxChars">
		/// The maximum number of characters to retrieve.
		/// </param>
		/// <returns>
		/// Returns the retrieved text.
		/// </returns>
		/// <remark>
		/// Warning: The performance of this function depends upon the number
		/// of retrieved characters and the complexity of the page.
		/// </remark>
		String^ GetContentAsText (int maxChars)
		{
			std::wstring resultString;
			webView->getContentAsText(resultString, maxChars);
			return gcnew String(resultString.c_str());
		}

		/// <summary>
		/// Zooms into the page, enlarging by 20%.
		/// </summary>
		void ZoomIn ()
		{
			webView->zoomIn();
		}

		/// <summary>
		/// Zooms out of the page, reducing by 20%.
		/// </summary>
		void ZoomOut ()
		{
			webView->zoomOut();
		}

		/// <summary>
		/// Resets the zoom level.
		/// </summary>
		void ResetZoom ()
		{
			webView->resetZoom();
		}

		/// <summary>
		/// Resizes this WebView to certain dimensions.
		/// </summary>
		/// <param name="width">
		/// The width to resize to.
		/// </param>
		/// <param name="height">
		/// The height to resize to.
		/// </param>
		void Resize (int width, int height)
		{
			webView->resize(width, height);
		}

		/// <summary>
		/// Injects a mouse-move event in local coordinates.
		/// </summary>
		/// <param name="x">
		/// The absolute x-coordinate of the mouse (localized to the WebView).
		/// </param>
		/// <param name="y">
		/// The absolute y-coordinate of the mouse (localized to the WebView).
		/// </param>
		void InjectMouseMove (int x, int y)
		{
			webView->injectMouseMove(x, y);
		}

		/// <summary>
		/// Injects a mouse-move event in local coordinates.
		/// </summary>
		/// <param name="position">
		/// The absolute coordinates of the mouse (localized to the WebView).
		/// </param>
		void InjectMouseMove (Point position)
		{
			webView->injectMouseMove(position.X, position.Y);
		}

		/// <summary>
		/// Injects a mouse-down event.
		/// </summary>
		/// <param name="button">
		/// The button that was pressed.
		/// </param>
		void InjectMouseDown (MouseButton button)
		{
			if (button == MouseButton::Left)
				webView->injectMouseDown(Awesomium::LEFT_MOUSE_BTN);
			else if (button == MouseButton::Right)
				webView->injectMouseDown(Awesomium::RIGHT_MOUSE_BTN);
			else if (button == MouseButton::Middle)
				webView->injectMouseDown(Awesomium::MIDDLE_MOUSE_BTN);
		}

		/// <summary>
		/// Injects a mouse-down event.
		/// </summary>
		/// <param name="button">
		/// The button that was pressed.
		/// </param>
		void InjectMouseDown (MouseButtons button)
		{
			if (button == MouseButtons::Left)
				webView->injectMouseDown(Awesomium::LEFT_MOUSE_BTN);
			else if (button == MouseButtons::Right)
				webView->injectMouseDown(Awesomium::RIGHT_MOUSE_BTN);
			else if (button == MouseButtons::Middle)
				webView->injectMouseDown(Awesomium::MIDDLE_MOUSE_BTN);
		}

		/// <summary>
		/// Injects a mouse-up event.
		/// </summary>
		/// <param name="button">
		/// The button that was released.
		/// </param>
		void InjectMouseUp (MouseButton button)
		{
			if (button == MouseButton::Left)
				webView->injectMouseUp(Awesomium::LEFT_MOUSE_BTN);
			else if (button == MouseButton::Right)
				webView->injectMouseUp(Awesomium::RIGHT_MOUSE_BTN);
			else if (button == MouseButton::Middle)
				webView->injectMouseUp(Awesomium::MIDDLE_MOUSE_BTN);
		}

		/// <summary>
		/// Injects a mouse-up event.
		/// </summary>
		/// <param name="button">
		/// The button that was released.
		/// </param>
		void InjectMouseUp (MouseButtons button)
		{
			if (button == MouseButtons::Left)
				webView->injectMouseUp(Awesomium::LEFT_MOUSE_BTN);
			else if (button == MouseButtons::Right)
				webView->injectMouseUp(Awesomium::RIGHT_MOUSE_BTN);
			else if (button == MouseButtons::Middle)
				webView->injectMouseUp(Awesomium::MIDDLE_MOUSE_BTN);
		}

		/// <summary>
		/// Injects a mouse-wheel event.
		/// </summary>
		/// <param name="scrollAmount">
		/// The amount of pixels to scroll by.
		/// </param>
		void InjectMouseWheel(int scrollAmount)
		{
			webView->injectMouseWheel(scrollAmount);
		}

#ifdef VERSION_108
		/// <summary>
		/// Injects a keyboard event.
		/// </summary>
		/// <remark>
		/// The native Windows keyboard message should be passed,
		/// msg can be one of the WM.* values.
		/// </remark>
		void InjectKeyboardEvent (IntPtr hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
		{
			webView->injectKeyboardEvent((HWND)hwnd.ToPointer(), msg, wParam, lParam);
		}

		/// <summary>
		/// Injects a keyboard event.
		/// </summary>
		/// <remark>
		/// The native Windows keyboard message should be passed.
		/// </remark>
		void InjectKeyboardEvent (IntPtr hwnd, WM msg, WPARAM wParam, LPARAM lParam)
		{
			webView->injectKeyboardEvent((HWND)hwnd.ToPointer(), (UINT)msg, wParam, lParam);
		}

		/// <summary>
		/// Injects a keyboard event.
		/// </summary>
		void InjectKeyboardEvent (Message^ message)
		{
			webView->injectKeyboardEvent(
				(HWND)message->HWnd.ToPointer(),
				message->Msg,
				(WPARAM)message->WParam.ToPointer(),
				(LPARAM)message->LParam.ToPointer());
		}
#else
		/// <summary>
		/// Injects a keyboard event.
		/// </summary>
		/// <param name="keyboardEvent">
		/// The keyboard event to inject.
		/// </param>
		void InjectKeyboardEvent (WebKeyboardEvent^ keyboardEvent)
		{
			webView->injectKeyboardEvent(keyboardEvent->ToNative());
		}
#endif

		/// <summary>
		/// Loads a URL into the WebView asynchronously.
		/// </summary>
		/// <param name="url">
		/// The URL to load.
		/// </param>
		void LoadUrl (Uri^ url)
		{
			String^ uri = url->ToString();
			LoadUrl(uri);
		}

		/// <summary>
		/// Loads a URL into the WebView asynchronously.
		/// </summary>
		/// <param name="url">
		/// The URL to load.
		/// </param>
		void LoadUrl (String^ url)
		{
			#ifdef VERSION_108
			std::string urlString;
			#else
			std::wstring urlString;
			#endif
			MarshalString(url, urlString);
			webView->loadURL(urlString);
		}

		/// <summary>
		/// Loads a URL into the WebView asynchronously.
		/// </summary>
		/// <param name="url">
		/// The URL to load.
		/// </param>
		/// <param name="frameName">
		/// The name of the frame to load the URL in; leave this blank to load in the main frame.
		/// </param>
		void LoadUrl (Uri^ url, String^ frameName)
		{
			String^ uri = url->ToString();
			LoadUrl(uri, frameName);
		}

		/// <summary>
		/// Loads a URL into the WebView asynchronously.
		/// </summary>
		/// <param name="url">
		/// The URL to load.
		/// </param>
		/// <param name="frameName">
		/// The name of the frame to load the URL in; leave this blank to load in the main frame.
		/// </param>
		void LoadUrl (String^ url, String^ frameName)
		{
			#ifdef VERSION_108
			std::string urlString;
			#else
			std::wstring urlString;
			#endif
			std::wstring frameNameString;
			MarshalString(url, urlString);
			MarshalString(frameName, frameNameString);
			webView->loadURL(urlString, frameNameString);
		}

		/// <summary>
		/// Loads a URL into the WebView asynchronously.
		/// </summary>
		/// <param name="url">
		/// The URL to load.
		/// </param>
		/// <param name="frameName">
		/// The name of the frame to load the URL in; leave this blank to load in the main frame.
		/// </param>
		/// <param name="userName">
		/// If the URL requires authentication, the username to authorize as.
		/// </param>
		/// <param name="password">
		/// If the URL requires authentication, the password to use.
		/// </param>
		void LoadUrl (Uri^ url, String^ frameName, String^ userName, String^ password)
		{
			String^ uri = url->ToString();
			LoadUrl(uri, frameName, userName, password);
		}

		/// <summary>
		/// Loads a URL into the WebView asynchronously.
		/// </summary>
		/// <param name="url">
		/// The URL to load.
		/// </param>
		/// <param name="frameName">
		/// The name of the frame to load the URL in; leave this blank to load in the main frame.
		/// </param>
		/// <param name="userName">
		/// If the URL requires authentication, the username to authorize as.
		/// </param>
		/// <param name="password">
		/// If the URL requires authentication, the password to use.
		/// </param>
		void LoadUrl (String^ url, String^ frameName, String^ userName, String^ password)
		{
			#ifdef VERSION_108
			std::string urlString;
			#else
			std::wstring urlString;
			#endif
			std::wstring frameNameString;
			std::string usernameString, passwordString;
			MarshalString(url, urlString);
			MarshalString(frameName, frameNameString);
			MarshalString(userName, usernameString);
			MarshalString(password, passwordString);
			webView->loadURL(urlString, frameNameString, usernameString, passwordString);
		}

		/// <summary>
		/// Loads a string of HTML into the WebView asynchronously.
		/// </summary>
		/// <param name="html">
		/// The HTML string to load.
		/// </param>
		/// <remark>
		/// The base directory (specified via WebCore.setBaseDirectory)
		/// will be used to resolve relative URL's resources (such as images, links, etc).
		/// </remark>
		void LoadHtml (String^ html)
		{
			#ifdef VERSION_108
			std::string htmlString;
			#else
			std::wstring htmlString;
			#endif
			MarshalString(html, htmlString);
			webView->loadHTML(htmlString, L"");
		}

		/// <summary>
		/// Loads a string of HTML into the WebView asynchronously.
		/// </summary>
		/// <param name="html">
		/// The HTML string to load.
		/// </param>
		/// <param name="frameName">
		/// The name of the frame to load the HTML in; leave this blank to load in the main frame.
		/// </param>
		/// <remark>
		/// The base directory (specified via WebCore.setBaseDirectory)
		/// will be used to resolve relative URL's resources (such as images, links, etc).
		/// </remark>
		void LoadHtml (String^ html, String^ frameName)
		{
			#ifdef VERSION_108
			std::string htmlString;
			#else
			std::wstring htmlString;
			#endif
			std::wstring frameNameString;
			MarshalString(html, htmlString);
			MarshalString(frameName, frameNameString);
			webView->loadHTML(htmlString, frameNameString);
		}

		/// <summary>
		/// Loads a local file into the WebView asynchronously.
		/// </summary>
		/// <param name="file">
		/// The file to load.
		/// </param>
		/// <remark>
		/// The file should exist within the base directory
		/// (specified via WebCore.setBaseDirectory).
		/// </remark>
		void LoadFile (String^ file)
		{
			std::string fileString;
			MarshalString(file, fileString);
			webView->loadFile(fileString);
		}

		/// <summary>
		/// Loads a local file into the WebView asynchronously.
		/// </summary>
		/// <param name="file">
		/// The file to load.
		/// </param>
		/// <param name="frameName">
		/// The name of the frame to load the file in; leave this blank to load in the main frame.
		/// </param>
		void LoadFile (String^ file, String^ frameName)
		{
			std::string fileString;
			std::wstring frameNameString;
			MarshalString(file, fileString);
			MarshalString(frameName, frameNameString);
			webView->loadFile(fileString, frameNameString);
		}

		/// <summary>
		/// Sets whether or not pages should be rendered with a transparent background-color.
		/// </summary>
		/// <param name="isTransparent">
		/// Whether or not to force the background-color as transparent.
		/// </param>
		void SetTransparent (bool isTransparent)
		{
			webView->setTransparent(isTransparent);
		}

		/// <summary>
		/// Renders the WebView to an off-screen buffer.
		/// </summary>
		/// <param name="destination">
		/// The buffer to render to, its width and height should match the WebView's.
		/// </param>
		/// <param name="rowSpan">
		/// The row-span of the destination buffer (number of bytes per row).
		/// </param>
		/// <param name="depth">
		/// The depth (bytes per pixel) of the destination buffer. Valid options
		/// include 3 (BGR/RGB) or 4 (BGRA/RGBA).
		/// </param>
		void Render (array<System::Byte>^ destination, int rowSpan, int depth)
		{
			pin_ptr<unsigned char> bufPin = &destination[0];
			Render((int)bufPin, rowSpan, depth);
		}

		/// <summary>
		/// Renders the WebView to an off-screen buffer.
		/// </summary>
		/// <param name="destination">
		/// The buffer to render to, its width and height should match the WebView's.
		/// </param>
		/// <param name="rowSpan">
		/// The row-span of the destination buffer (number of bytes per row).
		/// </param>
		/// <param name="depth">
		/// The depth (bytes per pixel) of the destination buffer. Valid options
		/// include 3 (BGR/RGB) or 4 (BGRA/RGBA).
		/// </param>
		void Render (int destination, int rowSpan, int depth)
		{
			webView->render((unsigned char*)destination, rowSpan, depth);
		}

		/// <summary>
		/// Renders the WebView to an off-screen buffer.
		/// </summary>
		/// <param name="destination">
		/// The buffer to render to, its width and height should match the WebView's.
		/// </param>
		/// <param name="rowSpan">
		/// The row-span of the destination buffer (number of bytes per row).
		/// </param>
		/// <param name="depth">
		/// The depth (bytes per pixel) of the destination buffer. Valid options
		/// include 3 (BGR/RGB) or 4 (BGRA/RGBA).
		/// </param>
		/// <param name="renderedRect">
		/// If asynchronous rendering is not enabled,	you can provide
		/// a pointer to a Rect to store the dimensions of the rendered area,
		/// or rather, the dimensions of the area that actually changed
		/// since the last render.
		/// </param>
		void Render (array<System::Byte>^ destination, int rowSpan, int depth, [Out] System::Drawing::Rectangle% renderedRect)
		{
			pin_ptr<unsigned char> bufPin = &destination[0];
			Render((int)bufPin, rowSpan, depth, renderedRect);
		}

		/// <summary>
		/// Renders the WebView to an off-screen buffer.
		/// </summary>
		/// <param name="destination">
		/// The buffer to render to, its width and height should match the WebView's.
		/// </param>
		/// <param name="rowSpan">
		/// The row-span of the destination buffer (number of bytes per row).
		/// </param>
		/// <param name="depth">
		/// The depth (bytes per pixel) of the destination buffer. Valid options
		/// include 3 (BGR/RGB) or 4 (BGRA/RGBA).
		/// </param>
		/// <param name="renderedRect">
		/// If asynchronous rendering is not enabled,	you can provide
		/// a pointer to a Rect to store the dimensions of the rendered area,
		/// or rather, the dimensions of the area that actually changed
		/// since the last render.
		/// </param>
		void Render (int destination, int rowSpan, int depth, [Out] System::Drawing::Rectangle% renderedRect)
		{
			Awesomium::Rect nativeRenderedRect;
			webView->render((unsigned char*)destination, rowSpan, depth, &nativeRenderedRect);
			renderedRect.X = nativeRenderedRect.x;
			renderedRect.Y = nativeRenderedRect.y;
			renderedRect.Width = nativeRenderedRect.width;
			renderedRect.Height = nativeRenderedRect.height;
		}

		/// <summary>
		/// Renders the WebView to an off-screen bitmap.
		/// </summary>
		/// <param name="width">
		/// Width of the render.
		/// </param>
		/// <param name="height">
		/// Height of the render.
		/// </param>
		System::Drawing::Bitmap^ WebView::RenderToBitmap(int width, int height)
		{
			System::Drawing::Bitmap^ bitmap = gcnew System::Drawing::Bitmap(
				width, height, System::Drawing::Imaging::PixelFormat::Format32bppArgb
			);

			System::Drawing::Imaging::BitmapData^ data = bitmap->LockBits(
				System::Drawing::Rectangle(0,0, width, height),
				System::Drawing::Imaging::ImageLockMode::WriteOnly,
				bitmap->PixelFormat
			);

			webView->render((unsigned char*)(void*)data->Scan0, data->Stride, 4);
			bitmap->UnlockBits(data);

			return bitmap;
		}

		/// <summary>
		/// Renders the WebView to an off-screen bitmap.
		/// </summary>
		/// <param name="width">
		/// Width of the render.
		/// </param>
		/// <param name="height">
		/// Height of the render.
		/// </param>
		/// <param name="pixelFormat">
		/// Format of the color data. Use Imaging.PixelFormat.Format32bppArgb or Format24bppRgb
		/// </param>
		System::Drawing::Bitmap^ WebView::RenderToBitmap(int width, int height, Imaging::PixelFormat pixelFormat)
		{
			int depth;

			switch(pixelFormat)
			{
			case Imaging::PixelFormat::Format24bppRgb:
				depth = 3;
				break;
			case Imaging::PixelFormat::Format32bppArgb:
				depth = 4;
				break;
			default:
				throw gcnew ArgumentException("Incompatible pixel format.");
			}

			System::Drawing::Bitmap^ bitmap = gcnew System::Drawing::Bitmap(
				width, height, pixelFormat
			);

			System::Drawing::Imaging::BitmapData^ data = bitmap->LockBits(
				System::Drawing::Rectangle(0,0, width, height),
				System::Drawing::Imaging::ImageLockMode::WriteOnly,
				bitmap->PixelFormat
			);

			webView->render((unsigned char*)(void*)data->Scan0, data->Stride, depth);
			bitmap->UnlockBits(data);

			return bitmap;
		}

		/// <summary>
		/// Renders the WebView to an off-screen bitmap.
		/// </summary>
		void WebView::RenderToBitmap(System::Drawing::Bitmap^ bitmap)
		{
			int depth;

			if (bitmap == nullptr)
				return;

			switch(bitmap->PixelFormat)
			{
			case Imaging::PixelFormat::Format24bppRgb:
				depth = 3;
				break;
			case Imaging::PixelFormat::Format32bppArgb:
				depth = 4;
				break;
			default:
				return;
			}

			System::Drawing::Imaging::BitmapData^ data = bitmap->LockBits(
				System::Drawing::Rectangle(0,0, bitmap->Width, bitmap->Height),
				System::Drawing::Imaging::ImageLockMode::WriteOnly,
				bitmap->PixelFormat
			);

			webView->render((unsigned char*)(void*)data->Scan0, data->Stride, depth);
			bitmap->UnlockBits(data);
		}

		/// <summary>
		/// Returns whether or not the current web-view is dirty and needs to be re-rendered.
		/// </summary>
		/// <returns>
		/// If the web-view is dirty, returns true, otherwise returns false.
		/// </returns>
		bool IsDirty()
		{
			return webView->isDirty();
		}

		/// <summary>
		/// Navigates back/forward in history via a relative offset.
		/// </summary>
		/// <param name="offset">
		/// The relative offset in history to navigate to.
		/// </param>
		/// <remarks>
		/// For example, to go back one page:
		///   myView->goToHistoryOffset(-1);
		/// Or, to go forward one page:
		///   myView->goToHistoryOffset(1);
		/// </remarks>
		void GoToHistoryOffset(int offset)
		{
			webView->goToHistoryOffset(offset);
		}

		/// <summary>
		/// Executes a string of Javascript in the context of the current page asynchronously.
		/// </summary>
		/// <param name="javaScript">
		/// The string of Javascript to execute.
		/// </param>
		void ExecuteJavaScript (String^ javaScript)
		{
			std::string javaScriptString;
			MarshalString(javaScript, javaScriptString);
			webView->executeJavascript(javaScriptString);
		}

		/// <summary>
		/// Executes a string of Javascript in the context of the current page asynchronously.
		/// </summary>
		/// <param name="javaScript">
		/// The string of Javascript to execute.
		/// </param>
		/// <param name="frameName">
		/// The name of the frame to execute in; leave this blank to execute in the main frame.
		/// </param>
		void ExecuteJavaScript (String^ javaScript, String^ frameName)
		{
			std::string javaScriptString;
			std::wstring frameNameString;
			MarshalString(javaScript, javaScriptString);
			MarshalString(frameName, frameNameString);
			webView->executeJavascript(javaScriptString, frameNameString);
		}

		/// <summary>
		/// Executes a string of Javascript in the context of the current page asynchronously with a result.
		/// </summary>
		/// <param name="javaScript">
		/// The string of Javascript to execute.
		/// </param>
		/// <returns>
		/// Returns a 'FutureJSValue' which is basically an 'IOU' for the future JSValue result.
		/// You can obtain the actual result via FutureJSValue::Get later.
		/// </returns>
		FutureJSValue^ ExecuteJavaScriptWithResult (String^ javaScript)
		{
			std::string javaScriptString;
			MarshalString(javaScript, javaScriptString);
			return gcnew FutureJSValue(
				webView->executeJavascriptWithResult(javaScriptString)
			);
		}

		/// <summary>
		/// Executes a string of Javascript in the context of the current page asynchronously with a result.
		/// </summary>
		/// <param name="javaScript">
		/// The string of Javascript to execute.
		/// </param>
		/// <param name="frameName">
		/// The name of the frame to execute in; leave this blank to execute in the main frame.
		/// </param>
		/// <returns>
		/// Returns a 'FutureJSValue' which is basically an 'IOU' for the future JSValue result.
		/// You can obtain the actual result via FutureJSValue::Get later.
		/// </returns>
		FutureJSValue^ ExecuteJavaScriptWithResult (String^ javaScript, String^ frameName)
		{
			std::string javaScriptString;
			std::wstring frameNameString;
			MarshalString(javaScript, javaScriptString);
			MarshalString(frameName, frameNameString);
			return gcnew FutureJSValue(
				webView->executeJavascriptWithResult(javaScriptString, frameNameString)
			);
		}

#ifndef VERSION_108
		/// <summary>
		/// Creates a new global Javascript object that will persist throughout the lifetime of this WebView.
		/// This object is managed directly by Awesomium and so you can modify its properties and bind
		/// callback functions via WebView.SetObjectProperty and WebView.etObjectCallback, respectively.
		/// </summary>
		/// <param name="objectName">
		/// The name of the object.
		/// </param>
		void CreateObject (String^ objectName)
		{
			std::wstring objectNameString;
			MarshalString(objectName, objectNameString);
			webView->createObject(objectNameString);
		}

		/// <summary>
		/// Destroys a Javascript object previously created by WebView::createObject.
		/// </summary>
		/// <param name="objectName">
		/// The name of the object to destroy.
		/// </param>
		void DestroyObject (String^ objectName)
		{
			std::wstring objectNameString;
			MarshalString(objectName, objectNameString);
			webView->destroyObject(objectNameString);
		}

		/// <summary>
		/// Call a certain function defined in Javascript directly.
		/// </summary>
		/// <param name="objectName">
		/// The name of the object that contains the function, pass an empty string if the function
		/// is defined in the global scope.
		/// </param>
		/// <param name="functionName">
		/// The name of the function.
		/// </param>
		/// <param name="args">
		/// The arguments to pass to the function.
		/// </param>
		void CallJavaScriptFunction (String^ objectName, String^ functionName, Collection<JSValue^>^ args)
		{
			Awesomium::JSArguments nativeArgs = Awesomium::JSArguments();
			for each (JSValue^ value in args)
			{
				nativeArgs.push_back(value->ToNative());
			}
			std::wstring objectString;
			std::wstring functionString;
			MarshalString(objectName, objectString);
			MarshalString(functionName, functionString);
			webView->callJavascriptFunction(objectString, functionString, nativeArgs);
		}

		/// <summary>
		/// Call a certain function defined in Javascript directly.
		/// </summary>
		/// <param name="objectName">
		/// The name of the object that contains the function, pass an empty string if the function
		/// is defined in the global scope.
		/// </param>
		/// <param name="functionName">
		/// The name of the function.
		/// </param>
		/// <param name="args">
		/// The arguments to pass to the function.
		/// </param>
		/// <param name="frameName">
		/// Optional, the name of the frame to execute in; leave this blank to execute in the main frame.
		/// </param>
		void CallJavaScriptFunction (String^ objectName, String^ functionName, Collection<JSValue^>^ args, String^ frameName)
		{
			Awesomium::JSArguments nativeArgs = Awesomium::JSArguments();
			int i;
			for (i=0; i<args->Count; i++)
			{
				nativeArgs.push_back(args[i]->ToNative());
			}
			std::wstring objectString;
			std::wstring functionString;
			std::wstring frameNameString;
			MarshalString(objectName, objectString);
			MarshalString(functionName, functionString);
			MarshalString(frameName, frameNameString);
			webView->callJavascriptFunction(objectString, functionString, nativeArgs, frameNameString);
		}
#endif

#ifdef VERSION_108
		/// <summary>
		/// Sets a global 'Client' property that can be accessed via Javascript from
		/// within all pages loaded into this web-view.
		/// </summary>
		/// <param name="name">
		/// The name of the property.
		/// </param>
		/// <param name="value">
		/// The javascript-value of the property.
		/// </param>
		/// <remarks>
		/// You can access all properties you set via the 'Client' object using Javascript.
		/// For example, if you set the property with a name of 'color' and a value of 'blue',
		/// you could access this from the page using
		/// Javascript: document.write("The color is " + Client.color);
		/// </remarks>
		void SetProperty(String^ name, JSValue^ value)
		{
			std::string nameString;
			Awesomium::JSValue nativevalue = value->ToNative();
			MarshalString(name, nameString);
			webView->setProperty(nameString, nativevalue);
		}
#else
		/// <summary>
		/// Sets a property of a Javascript object previously created by WebView.CreateObject.
		/// </summary>
		/// <param name="objectName">
		/// The name of the Javascript object.
		/// </param>
		/// <param name="propName">
		/// The name of the property.
		/// </param>
		/// <param name="value">
		/// The javascript-value of the property.
		/// </param>
		void SetObjectProperty(String^ objectName, String^ propName, JSValue^ value)
		{
			std::wstring objectNameString;
			std::wstring propNameString;
			Awesomium::JSValue nativevalue = value->ToNative();
			MarshalString(objectName, objectNameString);
			MarshalString(propName, propNameString);
			webView->setObjectProperty(objectNameString, propNameString, nativevalue);
		}
#endif

#ifdef VERSION_108
		/// <summary>
		/// Registers a global 'Client' callback that can be invoked via
		/// Javascript from within all pages loaded into this view.
		/// </summary>
		/// <param name="name">
		/// The name of the callback.
		/// </param>
		/// <remarks>
		/// You can invoke the callback in Javascript as:
		/// Client.your_name_here(arg1, arg2, ...);
		/// </remarks>
		void SetCallback (String^ name)
		{
			std::string nameString;
			MarshalString(name, nameString);
			webView->setCallback(nameString);
		}
#else
		/// <summary>
		/// Sets a callback function of a Javascript object previously created by WebView.CreateObject.
		/// This is very useful for passing events from Javascript. To receive notification of the callback,
		/// an OnCallback event handler should be registered (see WebView.OnCallback).
		/// </summary>
		/// <param name="objectName">
		/// The name of the Javascript object.
		/// </param>
		/// <param name="callbackName">
		/// The name of the callback function.
		/// </param>
		void SetObjectCallback (String^ objectName, String^ callbackName)
		{
			std::wstring objectNameString;
			std::wstring callbackNameString;
			MarshalString(objectName, objectNameString);
			MarshalString(callbackName, callbackNameString);
			webView->setObjectCallback(objectNameString, callbackNameString);
		}
#endif
	};
}
